/*
 * @FileName: [Uploader.java]
 * 
 * @Package com.bcinfo.upp.admin.util
 * 
 * 
 * Copyright (c) 2011-2015 BCINFO Technology Limited Com. All rights reserved.
 * 
 * This software is the confidential and proprietary information of BCINFO Technology Limited Company ("Confidential Information"). You shall not disclose such Confidential Information and shall use it only in accordance with the terms of the contract agreement you entered into with RKY.
 * 
 * $Rev: 44 $ $LastChangedDate: 2013-10-12 10:19:57 +0800 (星期六, 12 十月 2013) $ $LastChangedBy: wanggc $
 * 
 * @category bcinfo-upp
 * 
 * @version 1.1
 * 
 * @author $Author: wanggc $
 * 
 * Change History:[Formatter: author date description] <br/> 1 2 3
 */

package com.bcinfo.iccard.admin.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadBase.InvalidContentTypeException;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.log4j.Logger;

/**
 * 文件上传辅助类 <br/>
 * <p>
 * Description:<br/>
 * <p>
 * ueditor 文件上传辅助类
 * 
 * <p>
 * For Examples <br/>
 * 
 * <PRE>
 * Uploader up = new Uploader(request);
 * up.setSavePath(&quot;upload&quot;);
 * String[] fileType =
 * { &quot;.gif&quot;, &quot;.png&quot;, &quot;.jpg&quot;, &quot;.jpeg&quot;, &quot;.bmp&quot; };
 * up.setAllowFiles(fileType);
 * up.setMaxSize(10000); // 单位KB
 * up.upload();
 * response.getWriter().print(
 *         &quot;{'original':'&quot; + up.getOriginalName() + &quot;','url':'&quot; + up.getUrl() + &quot;','title':'&quot; + up.getTitle()
 *                 + &quot;','state':'&quot; + up.getState() + &quot;'}&quot;);
 * </PRE>
 * <p>
 */
public class UploaderHelper
{
    private static Logger           _log                   = Logger.getLogger(UploaderHelper.class);

    // 输出文件地址
    private String                  url                    = "";
    // 上传文件名
    private String                  fileName               = "";
    // 状态
    private String                  state                  = "";
    // 文件类型
    private String                  type                   = "";
    // 原始文件名
    private String                  originalName           = "";
    // 文件大小
    private String                  size                   = "";

    private HttpServletRequest      request                = null;
    private String                  title                  = "";

    // 保存路径
    private String                  savePath               = "upload";
    // 文件允许格式
    private String[]                allowFiles             =
                                                           { ".rar", ".doc", ".docx", ".zip", ".pdf", ".txt", ".swf",
            ".wmv", ".gif", ".png", ".jpg", ".jpeg", ".bmp" };
    // 文件大小限制，单位KB
    private int                     maxSize                = 5000;

    private String                  jsessionid;

    /**
     * @Fields prefix : 文件名前缀
     */
    private String                  prefix                 = "";

    /**
     * @Fields DEFAULT_FILENAME_SPLIT : 文件名分隔符
     */
    private static final String     DEFAULT_FILENAME_SPLIT = "_";

    private String                  dBstorePath;

    private HashMap<String, String> errorInfo              = new HashMap<String, String>();

    public UploaderHelper(HttpServletRequest request)
    {
        this.request = request;
        HashMap<String, String> tmp = this.errorInfo;
        tmp.put("SUCCESS", "SUCCESS"); // 默认成功
        tmp.put("NOFILE", "未包含文件上传域");
        tmp.put("TYPE", "不允许的文件格式");
        tmp.put("SIZE", "文件大小超出限制");
        tmp.put("ENTYPE", "请求类型ENTYPE错误");
        tmp.put("REQUEST", "上传请求异常");
        tmp.put("IO", "IO异常");
        tmp.put("DIR", "目录创建失败");
        tmp.put("UNKNOWN", "未知错误");

    }

    /**
     * 上传文件 <br/>
     * <p>
     * Description: 通过request参数处理文件上传 <br/>
     * <p>
     * Author: yun<br/>
     * <p>
     * Date: 2013-5-14-下午3:21:14<br/>
     * <p>
     * 
     */
    public void upload()
    {
        boolean isMultipart = ServletFileUpload.isMultipartContent(this.request);
        if (!isMultipart)
        {
            this.state = this.errorInfo.get("NOFILE");
            return;
        }
        DiskFileItemFactory dff = new DiskFileItemFactory();
        String savePath = this.getFolder(this.savePath);
        dff.setRepository(new File(savePath));
        try
        {
            ServletFileUpload sfu = new ServletFileUpload(dff);
            sfu.setSizeMax(this.maxSize * 1024);
            sfu.setHeaderEncoding("utf-8");
            FileItemIterator fii = sfu.getItemIterator(this.request);
            while (fii.hasNext())
            {
                FileItemStream fis = fii.next();
                if (!fis.isFormField())
                {
                    this.originalName = fis.getName().substring(
                            fis.getName().lastIndexOf(System.getProperty("file.separator")) + 1);
                    if (!this.checkFileType(this.originalName))
                    {
                        this.state = this.errorInfo.get("TYPE");
                        continue;
                    }
                    this.fileName = this.getName(this.originalName);
                    this.type = this.getFileExt(this.fileName);
                    this.url = dBstorePath + File.separator + this.fileName;
                    BufferedInputStream in = new BufferedInputStream(fis.openStream());
                    FileOutputStream out = new FileOutputStream(new File(this.getPhysicalPath(savePath)
                            + File.separator + this.fileName));
                    BufferedOutputStream output = new BufferedOutputStream(out);
                    Streams.copy(in, output, true);
                    this.state = this.errorInfo.get("SUCCESS");
                }
                else
                {
                    String fname = fis.getFieldName();
                    // 只处理title，其余表单请自行处理
                    if (!fname.equals("pictitle"))
                    {
                        continue;
                    }
                    BufferedInputStream in = new BufferedInputStream(fis.openStream());
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                    StringBuffer result = new StringBuffer();
                    while (reader.ready())
                    {
                        result.append((char) reader.read());
                    }
                    this.title = new String(result.toString().getBytes(), "utf-8");
                    reader.close();

                }
            }
        }
        catch (SizeLimitExceededException e)
        {
            this.state = this.errorInfo.get("SIZE");
            _log.error(ExceptionUtils.getStackTrace(e));
        }
        catch (InvalidContentTypeException e)
        {
            this.state = this.errorInfo.get("ENTYPE");
            _log.error(ExceptionUtils.getStackTrace(e));
        }
        catch (FileUploadException e)
        {
            this.state = this.errorInfo.get("REQUEST");
            _log.error(ExceptionUtils.getStackTrace(e));
        }
        catch (IOException e)
        {
            this.state = this.errorInfo.get("DIR");
            _log.error(ExceptionUtils.getStackTrace(e));
        }
        catch (Exception e)
        {
            this.state = this.errorInfo.get("UNKNOWN");
            _log.error(ExceptionUtils.getStackTrace(e));
        }
    }

    /**
     * 接受并保存以base64格式上传的文件
     * 
     * @param fieldName
     */
    public void uploadBase64(String fieldName)
    {
        String savePath = this.getFolder(this.savePath);
        String base64Data = this.request.getParameter(fieldName);
        this.fileName = this.getName("test.png");
        this.url = savePath + File.separator + this.fileName;
        Base64 decoder = new Base64();
        try
        {
            File outFile = new File(this.getPhysicalPath(this.url));
            OutputStream ro = new FileOutputStream(outFile);
            byte[] b = decoder.decode(base64Data);
            for (int i = 0; i < b.length; ++i)
            {
                if (b[i] < 0)
                {
                    b[i] += 256;
                }
            }
            ro.write(b);
            ro.flush();
            ro.close();
            this.state = this.errorInfo.get("SUCCESS");
        }
        catch (Exception e)
        {
            this.state = this.errorInfo.get("IO");
            _log.error(ExceptionUtils.getStackTrace(e));
        }
    }

    /**
     * 文件类型判断
     * 
     * @param fileName
     * @return
     */
    private boolean checkFileType(String fileName)
    {
        Iterator<String> type = Arrays.asList(this.allowFiles).iterator();
        while (type.hasNext())
        {
            String ext = type.next();
            if (fileName.toLowerCase().endsWith(ext))
            {
                return true;
            }
        }

        return false;
    }

    /**
     * 获取文件扩展名
     * 
     * @return string
     */
    private String getFileExt(String fileName)
    {
        return fileName.substring(fileName.lastIndexOf("."));
    }

    /**
     * 得到文件名称 <br/>
     * <p>
     * Description: 依据原始文件名生成新文件名 <br/>
     * <p>
     * Author: yun<br/>
     * <p>
     * Date: 2013-5-14-上午11:24:26<br/>
     * <p>
     * 
     * @param fileName
     * @return
     * 
     */
    private String getName(String fileName)
    {
        return this.fileName = prefix + DEFAULT_FILENAME_SPLIT + jsessionid + DEFAULT_FILENAME_SPLIT
                + System.currentTimeMillis() + this.getFileExt(fileName);
    }

    /**
     * 获得文件夹 <br/>
     * <p>
     * Description: 根据字符串创建本地目录 并按照日期建立子文件夹 <br/>
     * <p>
     * Author: yun<br/>
     * <p>
     * Date: 2013-5-14-上午11:23:31<br/>
     * <p>
     * 
     * @param path
     * @return
     * 
     */
    private String getFolder(String path)
    {
        File dir = new File(path);
        if (!dir.exists())
        {
            try
            {
                dir.mkdirs();
            }
            catch (Exception e)
            {
                this.state = this.errorInfo.get("DIR");
                return "";
            }
        }
        return path;
    }

    /**
     * 根据传入的虚拟路径获取物理路径
     * 
     * @param path
     * @return
     * @throws IOException
     */
    private String getPhysicalPath(String path) throws IOException
    {
        File file = new File(path);
        if (!file.exists())
        {
            FileUtils.forceMkdir(file);
        }

        return file.getPath();
    }

    public void setSavePath(String savePath)
    {
        this.savePath = savePath;
    }

    public void setAllowFiles(String[] allowFiles)
    {
        this.allowFiles = allowFiles;
    }

    public void setMaxSize(int size)
    {
        this.maxSize = size;
    }

    public String getSize()
    {
        return this.size;
    }

    public String getUrl()
    {
        return this.url;
    }

    public String getFileName()
    {
        return this.fileName;
    }

    public String getState()
    {
        return this.state;
    }

    public String getTitle()
    {
        return this.title;
    }

    public String getType()
    {
        return this.type;
    }

    public String getOriginalName()
    {
        return this.originalName;
    }

    /**
     * @return the jsessionid
     */
    public String getJsessionid()
    {
        return jsessionid;
    }

    /**
     * @param jsessionid
     *            the jsessionid to set
     */
    public void setJsessionid(String jsessionid)
    {
        this.jsessionid = jsessionid;
    }

    /**
     * @return the prefix
     */
    public String getPrefix()
    {
        return prefix;
    }

    /**
     * @param prefix
     *            the prefix to set
     */
    public void setPrefix(String prefix)
    {
        this.prefix = prefix;
    }

    /**
     * @return the dBstorePath
     */
    public String getdBstorePath()
    {
        return dBstorePath;
    }

    /**
     * @param dBstorePath
     *            the dBstorePath to set
     */
    public void setdBstorePath(String dBstorePath)
    {
        this.dBstorePath = dBstorePath;
    }
}